package com.thb.pay.agent.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.thb.pay.agent.entity.AgentAccount;
import com.thb.pay.agent.entity.AgentAccountHistory;
import com.thb.pay.agent.mapper.AgentAccountMapper;
import com.thb.pay.agent.service.AgentAccountHistoryService;
import com.thb.pay.agent.service.AgentAccountService;
import com.thb.pay.api.agent.vo.req.AgentAccountPageReq;
import com.thb.pay.api.agent.vo.resp.AgentAccountVo;
import com.thb.pay.core.exception.RRException;
import com.thb.pay.core.utils.Constant;
import com.thb.pay.core.utils.DateUtil;
import com.thb.pay.core.utils.PageResult;
import com.thb.pay.db.service.impl.BaseServiceImpl;
import com.thb.pay.db.utils.PageResultUtils;
import com.thb.pay.db.utils.PageUtils;
import com.thb.pay.db.utils.QueryUtils;
import com.xiaoleilu.hutool.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * <p>
 * 代理商-账户表 服务实现类
 * </p>
 *
 * @author lucky
 * @since 2019-07-21
 */
@Slf4j
@Service
public class AgentAccountServiceImpl extends BaseServiceImpl<AgentAccountMapper, AgentAccount> implements AgentAccountService {

    @Autowired
    private AgentAccountHistoryService agentAccountHistoryService;

    @Override
    public PageResult<AgentAccountVo> findPage(AgentAccountPageReq pageReq) {
        IPage<AgentAccount> currentPage = PageUtils.buildPage(pageReq);
        QueryWrapper wrapper = QueryUtils.buildQueryPage(pageReq);
        IPage<AgentAccount> pageResult = super.page(currentPage, wrapper);
        return PageResultUtils.build(pageResult, AgentAccountVo.class);
    }

    @Override
    public List<AgentAccountVo> listAll() {
        List<AgentAccountVo> targetResultList = Lists.newArrayList();
        List<AgentAccount> agentAccountList = super.selectList(null);
        if (CollUtil.isNotEmpty(agentAccountList)) {
            agentAccountList.forEach(agentAccount -> {
                AgentAccountVo targetResult = AgentAccountVo.build(AgentAccountVo.class);
                BeanUtils.copyProperties(agentAccount, targetResult);
                targetResultList.add(targetResult);
            });
        }
        return targetResultList;
    }

    @Override
    public AgentAccountVo getByAgentId(Long agentId) {
        Assert.notNull(agentId, "agentId.not.null");
        AgentAccount agentAccount = super.getById(agentId);
        if (agentAccount != null) {
            AgentAccountVo targetResult = AgentAccountVo.build(AgentAccountVo.class);
            BeanUtils.copyProperties(agentAccount, targetResult);
            return targetResult;
        }
        return null;
    }

    @Override
    public void increaseAmountToAccount(Long agentId, Long amount, Integer bizType, String bizItem, String orderId) {
        Assert.notNull(agentId, "agentId.not.null");
        Assert.notNull(amount, "amount.not.null");
        AgentAccount agentAccount = this.getAvailableAgentAccount(agentId);
        AgentAccount updateAgentAccount = new AgentAccount();
        updateAgentAccount.setId(agentAccount.getId());
        // 更新今日进出账,不为同一天,直接清零
        Date lastAccountUpdateDate = agentAccount.getUpdateTime(); // 账户修改时间
        if (!DateUtil.isSameDayWithToday(lastAccountUpdateDate)) {
            updateAgentAccount.setTodayExpend(0l);
            updateAgentAccount.setTodayIncome(0l);
        }
        // 总收益累加和今日收益
        if (AgentAccountHistory.AGENT_BIZ_TYPE_PROFIT == bizType) {// 业务类型是分润
            updateAgentAccount.setTotalIncome(agentAccount.getTotalIncome() + amount);
            /***** 根据上次修改时间，统计今日收益 *******/
            if (DateUtil.isSameDayWithToday(lastAccountUpdateDate)) {
                // 如果是同一天
                updateAgentAccount.setTodayIncome(updateAgentAccount.getTodayIncome() + amount);
            } else {
                // 不是同一天
                updateAgentAccount.setTodayIncome(amount);
            }
        }

        // 记录资金流水
        AgentAccountHistory agentAccountHistory = AgentAccountHistory.builder()
                .orderId(orderId)
                .agentId(agentId)
                .amount(amount)
                .balance(agentAccount.getBalance())
                .afterBalance(agentAccount.getBalance() + amount)
                .bizType(bizType)
                .bizItem(bizItem)
                .fundDirection(AgentAccountHistory.INCREASE_MONEY)
                .build();

        // 账户余额
        updateAgentAccount.setBalance(agentAccount.getBalance() + amount);
        // 账户可结算金额
        updateAgentAccount.setSettAmount(agentAccount.getSettAmount() + amount);


        //update事物锁【后期采用redis事物锁处理】
        LambdaUpdateWrapper<AgentAccount> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AgentAccount::getAgentId, agentId);
        updateWrapper.eq(AgentAccount::getBalance, agentAccount.getBalance());
        updateWrapper.eq(AgentAccount::getSettAmount, agentAccount.getSettAmount());
        boolean updateSuccess = super.update(agentAccount, updateWrapper);
        if (!updateSuccess) {
            throw RRException.build("account.increase.amount.fail");
        }
        agentAccountHistoryService.insert(agentAccountHistory);
    }


    @Override
    public void decreaseAmountToAccount(Long agentId, Long amount, Integer bizType, String bizItem) {
        Assert.notNull(agentId, "agentId.not.null");
        Assert.notNull(amount, "amount.not.null");
        AgentAccount agentAccount = getAvailableAgentAccount(agentId);

        Long availableBalance = agentAccount.getAvailableBalance();
        if (availableBalance.compareTo(amount) == -1) {
            // 余额不足减款
            throw RRException.build("agent.account.balance.out.limit");
        }

        AgentAccount updateAgentAccount = new AgentAccount();
        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = agentAccount.getUpdateTime(); // 应该修改为账户修改时间
        if (!DateUtil.isSameDayWithToday(lastUpdateDate)) {
            updateAgentAccount.setTodayExpend(amount);
            updateAgentAccount.setTodayIncome(0l);
        } else {
            updateAgentAccount.setTodayExpend(agentAccount.getTodayExpend() + amount);
        }
        updateAgentAccount.setTotalExpend(agentAccount.getTotalExpend() + amount);
        // 更新后账户余额
        updateAgentAccount.setBalance(agentAccount.getBalance() - amount);

        // 记录资金流水
        AgentAccountHistory agentAccountHistory = AgentAccountHistory.builder()
                .agentId(agentId)
                .amount(amount)
                .balance(agentAccount.getBalance())
                .afterBalance(agentAccount.getBalance() - amount)
                .bizType(bizType)
                .bizItem(bizItem)
                .fundDirection(AgentAccountHistory.DECREASE_MONEY)
                .build();


        //保证事物 更新成功【update锁】
        LambdaUpdateWrapper<AgentAccount> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AgentAccount::getAgentId, agentId);
        updateWrapper.eq(AgentAccount::getBalance, agentAccount.getBalance());
        boolean updateSuccess = super.update(agentAccount, updateWrapper);
        if (!updateSuccess) {
            throw RRException.build("account.decrease.amount.fail");
        }
        agentAccountHistoryService.insert(agentAccountHistory);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void freezeAmountToAccount(Long agentId, Long freezeAmount) {
        Assert.notNull(agentId, "agentId.not.null");
        Assert.notNull(freezeAmount, "freezeAmount.not.null");
        AgentAccount agentAccount = getAvailableAgentAccount(agentId);
        // 验证可用余额是否足够
        if (!agentAccount.availableBalanceIsEnough(freezeAmount)) {
            // 冻结金额超限
            throw RRException.build("freeze.amount.out.limit");
        }

        // 更新冻结金额时,保证更新时数据一致,使用update锁
        Long oldUnBalance = agentAccount.getUnBalance();//旧的金额 实际上应该带上金额查询进行更新
        BigDecimal newUnBalance = new BigDecimal(oldUnBalance).add(new BigDecimal(freezeAmount));
        AgentAccount updateAgentAccount = AgentAccount.builder()
                .unBalance(newUnBalance.longValue())
                .agentId(agentId)
                .build();

        LambdaUpdateWrapper<AgentAccount> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(AgentAccount::getAgentId, agentId);
        wrapper.eq(AgentAccount::getUnBalance, oldUnBalance);

        boolean updateSuccess = super.update(updateAgentAccount, wrapper);
        if (!updateSuccess) {
            throw RRException.build("account.freeze.amount.fail");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unFreezeAmountToAccount(Long agentId, Long unfreezeAmount, String requestNo, Integer bizType) {
        Assert.notNull(agentId, "agentId.not.null");
        Assert.notNull(unfreezeAmount, "unfreezeAmount.not.null");
        AgentAccount agentAccount = getAvailableAgentAccount(agentId);

        AgentAccount updateAgentAccount = new AgentAccount();
        // 判断解冻金额是否超限
        if (agentAccount.getUnBalance() < unfreezeAmount) {
            throw RRException.build("unfreeze.amount.out.limit");
        }

        // 记录资金流水
        AgentAccountHistory agentAccountHistory = AgentAccountHistory.builder()
                .agentId(agentId)
                .amount(unfreezeAmount)
                .balance(agentAccount.getBalance())
                .afterBalance(agentAccount.getBalance() - unfreezeAmount)
                .bizType(bizType)
                .fundDirection(AgentAccountHistory.DECREASE_MONEY)
                .build();


        // 更新账户
        LambdaUpdateWrapper<AgentAccount> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AgentAccount::getAgentId, agentId);
        updateWrapper.eq(AgentAccount::getUnBalance, agentAccount.getUnBalance());
        updateWrapper.eq(AgentAccount::getBalance, agentAccount.getBalance());
        updateWrapper.eq(AgentAccount::getSettAmount, agentAccount.getSettAmount());

        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = agentAccount.getUpdateTime(); // 账户修改时间
        if (!DateUtil.isSameDayWithToday(lastUpdateDate)) {
            agentAccount.setTodayExpend(unfreezeAmount);
            agentAccount.setTodayIncome(0l);
        } else {
            agentAccount.setTodayExpend(agentAccount.getTodayExpend() + unfreezeAmount);
        }
        updateAgentAccount.setBalance(agentAccount.getBalance() - unfreezeAmount);// 减款
        updateAgentAccount.setUnBalance(agentAccount.getUnBalance() - unfreezeAmount);// 解冻
        updateAgentAccount.setSettAmount(agentAccount.getSettAmount() - unfreezeAmount);// 减少可结算金额


        boolean updateSuccess = super.update(updateAgentAccount, updateWrapper);
        if (!updateSuccess) {
            throw RRException.build("account.unfreeze.amount.fail");
        }
        agentAccountHistoryService.insert(agentAccountHistory);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void unFreezeSettAmountToAccount(Long agentId, Long unfreezeAmount) {
        Assert.notNull(agentId, "agentId.not.null");
        Assert.notNull(unfreezeAmount, "unfreezeAmount.not.null");
        AgentAccount agentAccount = getAvailableAgentAccount(agentId);
        // 判断解冻金额是否超限
        if (agentAccount.getUnBalance() < unfreezeAmount) {
            throw RRException.build("unfreeze.amount.out.limit");
        }
        AgentAccount updateAgentAccount = new AgentAccount();
        // 更新今日进出账,不为同一天,直接清零
        Date lastUpdateDate = agentAccount.getUpdateTime(); // 账户修改时间
        if (!DateUtil.isSameDayWithToday(lastUpdateDate)) {
            updateAgentAccount.setTodayExpend(0l);
            updateAgentAccount.setTodayIncome(0l);
        }
        // 更新账户不可用金额
        LambdaUpdateWrapper<AgentAccount> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AgentAccount::getAgentId, agentId);
        updateWrapper.eq(AgentAccount::getUnBalance, agentAccount.getUnBalance());

        updateAgentAccount.setUnBalance(agentAccount.getUnBalance() - unfreezeAmount);
        boolean updateSuccess = super.update(updateAgentAccount, updateWrapper);
        if (!updateSuccess) {
            // 更新失败,抛出异常
            throw RRException.build("account.amount.update.fail");
        }
    }


    /**
     * @param agentId
     * @Description 获取有效的代理商账号
     * @Author lucky
     * @data 2019/7/26 0:14
     */
    private AgentAccount getAvailableAgentAccount(Long agentId) {
        AgentAccount agentAccount = super.getById(agentId);
        if (agentAccount == null || Constant.PUB_YES.equals(agentAccount.getStatus())) {
            // 账户不存在
            throw RRException.build(400, "agent.account.not.exist");
        }
        return agentAccount;
    }
}
